#include <iostream>
#include <vector>
#include <boost/shared_ptr.hpp>
#include <boost/weak_ptr.hpp>
#include <boost/enable_shared_from_this.hpp>

// ------ Observable ------
class Observer;
class Observable {
public:
    void register_(boost::weak_ptr<Observer> x);
    void notifyAll();
private:
    std::vector<boost::weak_ptr<Observer>> _observers;
};

// ------ Observer ------
class Observer : public boost::enable_shared_from_this<Observer>{
public:
    Observer(): _subject(NULL) {}
    virtual ~Observer() {
        // 此处析构也不是原子的，如果析构一半怎么办？
        /*
        if (_subject) {
            // _subject 真的还在吗？
            std::cout << "unregister" << std::endl;
            _subject->unregister(this);
        }
        */
    }
    virtual void update() = 0;
    void observe(Observable *s) {
        s->register_(shared_from_this());
        _subject = s;
    }
private:
    Observable* _subject;
};

void Observable::register_(boost::weak_ptr<Observer> x) {
    _observers.push_back(x);
}

void Observable::notifyAll() {
    typedef std::vector<boost::weak_ptr<Observer>>::iterator Iterator;
    Iterator it = _observers.begin();
    while(it != _observers.end()) {
        boost::shared_ptr<Observer> x = it->lock();
        // 有了 weak_ptr 这个代理帮助我们监控对象是否还在，一切变得相当方便
        if (x) {
            x->update();
            ++it;
        } else {
            it = _observers.erase(it);
            std::cout << "-------- erase --------" << std::endl;
        }
    }
}

// ------ Foo ------
class Foo: public Observer {
public:
    Foo(const char* name): _name(name) {}
    ~Foo() {};
    void update() {
        std::cout << _name << " is notified" << std::endl;
    }
private:
    std::string _name;
};

// ------ main ------
int main() {
    Observable *observable = new Observable();
    {
        boost::shared_ptr<Foo> f1(new Foo("allen"));
        boost::shared_ptr<Foo> f2(new Foo("luffy"));
        f1->observe(observable);
        f2->observe(observable);
    }
    boost::shared_ptr<Foo> f3(new Foo("zoro"));
    boost::shared_ptr<Foo> f4(new Foo("sanji"));

    f3->observe(observable);
    f4->observe(observable);

    observable->notifyAll();

    delete observable;
}
